import json
import os
from core.services.data_manager import DataManager
from core.utils.logger import debug, info, warning, error
from core.utils.validators import validate_cart_item
from core.utils.error_handler import handle_critical_errors, handle_warnings


class CartItem:
    def __init__(self, product_data, quantity=1, product_obj=None):
        # product_data is a serializable dict snapshot; product_obj is optional runtime product instance
        self.product_data = product_data
        self.quantity = quantity
        self._product_obj = product_obj

    @property
    def product(self):
        # prefer live object if available
        if self._product_obj is not None:
            return self._product_obj
        # otherwise, create a lightweight object from product_data
        class P:
            pass

        p = P()
        for k, v in self.product_data.items():
            setattr(p, k, v)
        return p

    @property
    def subtotal(self):
        return self.product.price * self.quantity


class ShoppingCart:
    def __init__(self, controller=None, user_id=None):
        self.items = []
        self.controller = controller
        self.user_id = user_id or "guest"  # 默认使用guest作为游客ID
        # try loading persisted cart
        try:
            self._load()
        except Exception:
            self.items = []

    def _cart_file(self):
        dm = DataManager()
        return os.path.join(dm.data_dir, f'cart_{self.user_id}.json')

    def _load(self):
        path = self._cart_file()
        if not os.path.exists(path):
            self.items = []
            return
        with open(path, 'r', encoding='utf-8') as f:
            data = json.load(f) or []
        items = []
        for it in data:
            items.append(CartItem(it.get('product', {}), quantity=it.get('quantity', 1)))
        self.items = items

    def _save(self):
        path = self._cart_file()
        data = []
        for it in self.items:
            data.append({'product': getattr(it, 'product_data', {}) or getattr(it.product, 'to_dict', lambda: {})(), 'quantity': it.quantity})
        os.makedirs(os.path.dirname(path), exist_ok=True)
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

    @handle_critical_errors("Failed to add item to cart")
    def add_item(self, product, quantity=1):
        # product can be an object with product_id, or a dict
        pid = getattr(product, 'product_id', None) or product.get('product_id')
        product_name = getattr(product, 'name', 'Unknown')
        
        debug(f"Adding item to cart: {product_name} (ID: {pid}) x{quantity}")
        
        # 验证购物车商品数据
        is_valid, errors = validate_cart_item(pid, quantity)
        if not is_valid:
            error_msg = "; ".join(errors)
            warning(f"Cart item validation failed: {product_name} - {error_msg}")
            raise ValueError(error_msg)
        
        # 检查库存是否足够
        if self.controller and hasattr(self.controller, 'product_service'):
            if not self.controller.product_service.check_stock_availability(pid, quantity):
                warning(f"Insufficient stock, cannot add item: {product_name} x{quantity}")
                raise ValueError(f"Insufficient stock, cannot add {quantity} items")
        
        for item in self.items:
            if getattr(item.product, 'product_id', None) == pid:
                # 检查增加数量后是否超过库存
                if self.controller and hasattr(self.controller, 'product_service'):
                    total_quantity = item.quantity + quantity
                    if not self.controller.product_service.check_stock_availability(pid, total_quantity):
                        raise ValueError(f"Insufficient stock, current stock cannot meet the demand for {total_quantity} items")
                item.quantity += quantity
                self._save()
                return
        # store a snapshot of product as dict
        pdata = product.to_dict() if hasattr(product, 'to_dict') else dict(product)
        self.items.append(CartItem(pdata, quantity=quantity, product_obj=product))
        self._save()

    def remove_item(self, product_id):
        self.items = [item for item in self.items if getattr(item.product, 'product_id', None) != product_id]
        self._save()

    @handle_critical_errors("Failed to update item quantity")
    def update_quantity(self, product_id, quantity):
        for item in self.items:
            if getattr(item.product, 'product_id', None) == product_id:
                if quantity <= 0:
                    self.remove_item(product_id)
                else:
                    # 检查库存是否足够
                    if self.controller and hasattr(self.controller, 'product_service'):
                        if not self.controller.product_service.check_stock_availability(product_id, quantity):
                            raise ValueError(f"Insufficient stock, cannot set quantity to {quantity}")
                    item.quantity = quantity
                    self._save()
                return

    @property
    def total_price(self):
        return sum(item.subtotal for item in self.items)

    @property
    def total_items(self):
        return sum(item.quantity for item in self.items)

    def clear(self):
        self.items = []
        self._save()
    
    def update_user_id(self, new_user_id):
        """Update user ID, used when user logs in to switch cart"""
        if new_user_id == self.user_id:
            return  # 相同用户ID，无需切换
        
        # 保存当前购物车
        self._save()
        
        # 如果是游客切换到登录用户，需要合并购物车
        if self.user_id == "guest" and new_user_id != "guest":
            self._merge_guest_cart_to_user(new_user_id)
        else:
            # 更新用户ID
            old_user_id = self.user_id
            self.user_id = new_user_id or "guest"
            
            # 加载新用户的购物车
            try:
                self._load()
            except Exception:
                self.items = []
    
    @handle_warnings("Issues occurred when merging carts, some items may not be merged")
    def _merge_guest_cart_to_user(self, user_id):
        """Merge guest cart into user cart"""
        try:
            # 保存当前游客购物车
            guest_items = self.items.copy()
            
            # 更新用户ID
            self.user_id = user_id
            
            # 加载用户购物车
            try:
                self._load()
            except Exception:
                self.items = []
            
            # 合并购物车商品
            merged_items = self.items.copy()
            
            for guest_item in guest_items:
                product_id = getattr(guest_item.product, 'product_id', None)
                if not product_id:
                    continue
                
                # 检查用户购物车中是否已有该商品
                found_existing = False
                for user_item in merged_items:
                    if getattr(user_item.product, 'product_id', None) == product_id:
                        # 合并数量
                        try:
                            # 检查合并后的库存是否足够
                            if self.controller and hasattr(self.controller, 'product_service'):
                                total_quantity = user_item.quantity + guest_item.quantity
                                if self.controller.product_service.check_stock_availability(product_id, total_quantity):
                                    user_item.quantity = total_quantity
                                    info(f"Merging item {guest_item.product.name}: {user_item.quantity - guest_item.quantity} + {guest_item.quantity} = {user_item.quantity}")
                                else:
                                    warning(f"Insufficient stock, cannot fully merge item {guest_item.product.name}")
                                    # 尝试合并到最大可用数量
                                    max_available = 0
                                    for qty in range(guest_item.quantity, 0, -1):
                                        if self.controller.product_service.check_stock_availability(product_id, user_item.quantity + qty):
                                            max_available = qty
                                            break
                                    if max_available > 0:
                                        user_item.quantity += max_available
                                        info(f"Partially merged item {guest_item.product.name}: added {max_available} items")
                                    else:
                                        warning(f"Cannot merge item {guest_item.product.name}, insufficient stock")
                            else:
                                # 没有库存检查，直接合并
                                user_item.quantity += guest_item.quantity
                                info(f"Merging item {guest_item.product.name}: total quantity {user_item.quantity}")
                        except Exception as e:
                            error(f"合并商品 {guest_item.product.name} 时出错: {e}")
                        found_existing = True
                        break
                
                if not found_existing:
                    # 用户购物车中没有该商品，直接添加
                    try:
                        # 检查库存是否足够
                        if self.controller and hasattr(self.controller, 'product_service'):
                            if self.controller.product_service.check_stock_availability(product_id, guest_item.quantity):
                                merged_items.append(guest_item)
                                info(f"Adding new item to user cart: {guest_item.product.name}")
                            else:
                                warning(f"Insufficient stock, cannot add item {guest_item.product.name}")
                        else:
                            # 没有库存检查，直接添加
                            merged_items.append(guest_item)
                            info(f"Adding new item to user cart: {guest_item.product.name}")
                    except Exception as e:
                        error(f"Error adding item {guest_item.product.name} to user cart: {e}")
            
            # 更新购物车
            self.items = merged_items
            self._save()
            
            info(f"Successfully merged guest cart into user {user_id}, total {len(merged_items)} items")
            
        except Exception as e:
            error(f"Error merging cart: {e}")
            # 如果合并失败，至少确保用户ID正确更新
            self.user_id = user_id
            try:
                self._load()
            except Exception:
                self.items = []
    
    def get_user_id(self):
        """Get current user ID"""
        return self.user_id
    
    def process_purchase(self):
        """Process purchase, reduce stock and clear cart
        
        Returns:
            bool: Whether the process succeeded
        """
        if not self.controller or not hasattr(self.controller, 'product_service'):
            return False
        
        # 检查所有商品库存是否足够
        for item in self.items:
            if not self.controller.product_service.check_stock_availability(
                item.product.product_id, item.quantity
            ):
                return False
        
        # 减少库存
        for item in self.items:
            success = self.controller.product_service.update_stock(
                item.product.product_id, -item.quantity
            )
            if not success:
                # 如果某个商品库存更新失败，回滚之前的更新
                # 这里简化处理，实际应该实现事务回滚
                return False
        
        # 清空购物车
        self.clear()
        return True